﻿Imports System.Windows.Controls
Imports System.Windows
Imports System.Windows.Data

Public Class Validator
    Inherits ValidationRule
#Region "Validation Attached Property"

    Public Shared ReadOnly LowerRangeProperty As DependencyProperty _
        = DependencyProperty.RegisterAttached("LowerRange", GetType(Double), GetType(Validator),
                                              New UIPropertyMetadata(Double.NaN))

    Public Shared ReadOnly UpperRangeProperty As DependencyProperty _
        = DependencyProperty.RegisterAttached("UpperRange", GetType(Double), GetType(Validator),
                                              New UIPropertyMetadata(Double.NaN))

    Public Shared ReadOnly ValidationTypeProperty As DependencyProperty _
        = DependencyProperty.RegisterAttached("ValidationType", GetType(Type), GetType(ValidationRule),
                                              New UIPropertyMetadata(Nothing, AddressOf OnValidationTypeChanged))

    Public Shared Function GetUpperRange(obj As DependencyObject) As Double
        Return CDbl(obj.GetValue(UpperRangeProperty))
    End Function

    Public Shared Sub SetUpperRange(obj As DependencyObject, value As Double)
        obj.SetValue(UpperRangeProperty, value)
    End Sub

    ' Using a DependencyProperty as the backing store for UpperRange.  This enables animation, styling, binding, etc...
    Public Shared Function GetLowerRange(obj As DependencyObject) As Double
        Return CDbl(obj.GetValue(LowerRangeProperty))
    End Function

    Public Shared Sub SetLowerRange(obj As DependencyObject, value As Double)
        obj.SetValue(LowerRangeProperty, value)
    End Sub

    ' Using a DependencyProperty as the backing store for LowerRange.  This enables animation, styling, binding, etc...
    Public Shared Function GetValidationType(obj As DependencyObject) As Type
        Return DirectCast(obj.GetValue(ValidationTypeProperty), Type)
    End Function

    Public Shared Sub SetValidationType(obj As DependencyObject, value As Type)
        obj.SetValue(ValidationTypeProperty, value)
    End Sub

    Public Shared Function GetNoEmptyValue(obj As DependencyObject) As Boolean
        Return CBool(obj.GetValue(NoEmptyValueProperty))
    End Function

    Public Shared Sub SetNoEmptyValue(obj As DependencyObject, value As Boolean)
        obj.SetValue(NoEmptyValueProperty, value)
    End Sub

    ' Using a DependencyProperty as the backing store for NoEmptyValue.  This enables animation, styling, binding, etc...
    Public Shared ReadOnly NoEmptyValueProperty As DependencyProperty = DependencyProperty.RegisterAttached("NoEmptyValue", GetType(Boolean), GetType(Validator), New UIPropertyMetadata(False, AddressOf OnValidationTypeChanged))

    ' Using a DependencyProperty as the backing store for ValidationType.  This enables animation, styling, binding, etc...
    Private Shared Sub OnValidationTypeChanged(obj As DependencyObject, args As DependencyPropertyChangedEventArgs)
        Dim element = TryCast(obj, FrameworkElement)
        If element Is Nothing Then
            Return
        End If

        ' When the element has loaded.
        AddHandler element.Loaded, Function(s, e)
                                       ' Create a new validator
                                       Dim validation = New Validator(GetValidationType(obj), GetUpperRange(obj), GetLowerRange(obj), GetNoEmptyValue(obj))
                                       ' Get the binding expression for the textbox property
                                       Dim binding As Windows.Data.Binding = BindingOperations.GetBinding(obj, TextBox.TextProperty)
                                       ' If not null and doesn't already contain the validator, then add it.
                                       If binding IsNot Nothing Then
                                           If Not binding.ValidationRules.Contains(validation) Then
                                               binding.ValidationRules.Add(validation)
                                           End If
                                       End If
                                   End Function
    End Sub

#End Region

#Region "Validation"

    Public Sub New()
    End Sub

    Public Sub New(validationType__1 As Type)
        ValidationType = validationType__1
    End Sub

    Public Sub New(validationType__1 As Type, upperRange__2 As Double, lowerRange__3 As Double, noEmptyValue__4 As Boolean)
        ValidationType = validationType__1
        UpperRange = upperRange__2
        LowerRange = lowerRange__3
        NoEmptyValue = noEmptyValue__4
    End Sub

    Public Property ValidationType() As Type
        
    Public Property UpperRange() As Double

    Public Property LowerRange() As Double
        
    Public Property NoEmptyValue() As Boolean
        

    Public Overrides Function Validate(value As Object, cultureInfo As Globalization.CultureInfo) As ValidationResult
        Dim valid As Boolean = True
        Dim [error] As String = String.Empty
        Try
            ' Try to convert to the type specified
            If ValidationType IsNot Nothing Then
                Convert.ChangeType(value, ValidationType)
            End If

            ' compare for upper range and lower range
            Dim result As Double
            If Double.TryParse(value.ToString(), result) Then
                If UpperRange <> Double.NaN Then
                    If result > UpperRange Then
                        valid = False
                        [error] += String.Format("Value must be less than {0}. ", UpperRange)
                    End If
                End If
                If LowerRange <> Double.NaN Then
                    If result < LowerRange Then
                        valid = False
                        [error] += String.Format("Value must be greater than {0}. ", LowerRange)
                    End If
                End If
            End If

            If NoEmptyValue Then
                If String.IsNullOrEmpty(value.ToString().Trim()) Then
                    valid = False
                    [error] += "Value must not be empty. "
                End If
            End If
        Catch generatedExceptionName As Exception
            ' return invalid type error.
            valid = False
            [error] += "Value is not of type " & Convert.ToString(ValidationType.FullName)
        End Try

        Return New ValidationResult(valid, [error])
    End Function

#End Region
End Class
